package com.ruoyi.system.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.system.domain.Project;
import com.ruoyi.system.dto.ContructionProjectAddOrUpdateDTO;
import com.ruoyi.system.dto.ContructionProjectDTO;
import com.ruoyi.system.dto.ContructionProjectQueryDTO;
import com.ruoyi.system.dto.convert.ContructionProjectConverter;
import com.ruoyi.system.dto.convert.ProjectItemConverter;
import com.ruoyi.system.dto.convert.StageConverter;
import com.ruoyi.system.entity.ConstructionProjectDO;
import com.ruoyi.system.entity.ConstructionStageDO;
import com.ruoyi.system.entity.ConstructionProjectStageRelDO;
import com.ruoyi.system.entity.ConstructionProjectItemDO;
import com.ruoyi.system.mapper.ConstructionProjectMapper;
import com.ruoyi.system.mapper.ConstructionProjectStageRelMapper;
import com.ruoyi.system.service.*;
import com.ruoyi.system.vo.ProjectDetailVO;
import com.ruoyi.system.vo.StageDetailVO;
import com.ruoyi.system.vo.TaskDetailVO;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class ConstructionProjectServiceImpl extends ServiceImpl<ConstructionProjectMapper, ConstructionProjectDO>
        implements ConstructionProjectService {


    @Resource
    private ConstructionProjectStageRelMapper constructionProjectStageRelMapper;

    @Resource
    private IProjectStageService projectStageService;

    @Resource
    private ConstructionStageService stageService;

    @Resource
    private ProjectService projectService;

    @Resource
    private IConstructionItemService projectItemService;

    /**
     * 日期格式化（年份）
     */
    private static final DateTimeFormatter YEAR_FORMATTER = DateTimeFormatter.ofPattern("yyyy");

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String addProject(ContructionProjectAddOrUpdateDTO dto, String operator) {
        // 1. 校验参数（日期逻辑）
        validateProjectDate(dto.getStartDate(), dto.getExpectedCheckinDate());

        // 2. 校验阶段合法性（是否存在、是否启用）
        List<Long> stageIdList = dto.getStageIdList();
        if (CollectionUtils.isEmpty(stageIdList)) {
            throw new ServiceException("请至少选择一项施工范围");
        }
        boolean stageValid = stageService.checkStagesValid(stageIdList);
        if (!stageValid) {
            throw new ServiceException("部分施工阶段不存在或已停用，请重新选择");
        }

        // 3. 生成施工编号
        String projectCode = generateProjectCode();

        // 4. 构建项目DO并保存
        ConstructionProjectDO projectDO = convertToDO(dto);
        projectDO.setProjectCode(projectCode);
        projectDO.setCreateBy(operator);
        projectDO.setUpdateBy(operator);
        projectDO.setProjectStatus(1); // 默认状态：进行中
        save(projectDO);

        // 5. 批量插入项目-阶段关联关系
        List<ConstructionProjectStageRelDO> relList = stageIdList.stream().map(stageId -> {
            ConstructionProjectStageRelDO relDO = new ConstructionProjectStageRelDO();
            relDO.setProjectId(projectDO.getId());
            relDO.setStageId(stageId);
            relDO.setStageStatus(0); // 初始状态：未开始
            return relDO;
        }).collect(Collectors.toList());
        constructionProjectStageRelMapper.batchInsert(relList);

        return projectCode;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateProject(ContructionProjectAddOrUpdateDTO dto, String operator) {
        // 1. 校验项目是否存在
        Long projectId = dto.getId();
        ConstructionProjectDO existProject = getById(projectId);
        if (existProject == null) {
            throw new ServiceException("施工项目不存在");
        }

        // 2. 校验日期逻辑
//        validateProjectDate(dto.getStartDate(), dto.getExpectedCheckinDate());

        // 3. 处理阶段变更（先查已关联阶段，对比差异）
        List<Long> newStageIdList = dto.getStageIdList();
        List<Long> oldStageIdList = baseMapper.getStageIdsByProjectId(projectId);
        // 3.1 查已完成的阶段（不可删除）
        List<Long> completedStageIds = constructionProjectStageRelMapper.getCompletedStageIds(projectId);

        // 3.2 计算需删除的阶段（旧有存在、新无、且未完成）
        List<Long> deleteStageIds = oldStageIdList.stream()
                .filter(stageId -> !newStageIdList.contains(stageId) && !completedStageIds.contains(stageId))
                .collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(deleteStageIds)) {
            constructionProjectStageRelMapper.deleteByProjectIdAndStageIds(projectId, deleteStageIds);
        }

        // 3.3 计算需新增的阶段（新有、旧无、且合法）
        List<Long> addStageIds = newStageIdList.stream()
                .filter(stageId -> !oldStageIdList.contains(stageId))
                .collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(addStageIds)) {
            boolean stageValid = stageService.checkStagesValid(addStageIds);
            if (!stageValid) {
                throw new ServiceException("部分新增施工阶段不存在或已停用，请重新选择");
            }
            List<ConstructionProjectStageRelDO> relList = addStageIds.stream().map(stageId -> {
                ConstructionProjectStageRelDO relDO = new ConstructionProjectStageRelDO();
                relDO.setProjectId(projectId);
                relDO.setStageId(stageId);
                relDO.setStageStatus(0); // 初始状态：未开始
                return relDO;
            }).collect(Collectors.toList());
            constructionProjectStageRelMapper.batchInsert(relList);
        }

        // 4. 更新项目基础信息
        ConstructionProjectDO updateDO = convertToDO(dto);
        updateDO.setId(projectId);
        updateDO.setProjectCode(existProject.getProjectCode()); // 施工编号不可修改
        updateDO.setUpdateBy(operator);
        updateById(updateDO);
    }

    @Override
    public IPage<ContructionProjectDTO> queryProjectPage(ContructionProjectQueryDTO queryDTO) {
        IPage<ConstructionProjectDO> page = new Page<>(queryDTO.getPageNum(), queryDTO.getPageSize());
        IPage<ContructionProjectDTO> projectListDTOIPage = baseMapper.queryProjectPage(page, queryDTO);
        List<ContructionProjectDTO> records = projectListDTOIPage.getRecords();
        if (!records.isEmpty()) {
            List<Long> projectId = records.stream().map(ContructionProjectDTO::getId).collect(Collectors.toList());
            List<ConstructionProjectStageRelDO> list = projectStageService.lambdaQuery()
                    .in(ConstructionProjectStageRelDO::getProjectId, projectId)
                    .eq(ConstructionProjectStageRelDO::getStageStatus, 2)
                    .list();// 已完成阶段
            if (!list.isEmpty()) {
                List<Long> stageIds = list.stream().map(ConstructionProjectStageRelDO::getStageId).collect(Collectors.toList());
                List<ConstructionStageDO> list1 = stageService.lambdaQuery()
                        .in(ConstructionStageDO::getId, stageIds)
                        .list();
                Map<Long, String> collect =
                        list1.stream().collect(Collectors.toMap(ConstructionStageDO::getId, ConstructionStageDO::getStageName));
                records.forEach(x -> {
                    StringBuffer sb = new StringBuffer();
                    list.forEach( y -> {
                        if (y.getProjectId().equals(x.getId())) {
                            String s = collect.get(y.getStageId());
                            if (sb.length() == 0) {
                                sb.append(s);
                            } else {
                                sb.append(",").append(s);
                            }
                        }
                    });
                    x.setOngoingStages(sb.toString());
                });
                projectListDTOIPage.setRecords(records);
            }
        }
        return projectListDTOIPage;
    }

    @Override
    public ProjectDetailVO getProjectDetail(Long projectId) {
        // 1. 查询项目基础信息
        ConstructionProjectDO projectDO = getById(projectId);
        if (projectDO == null) {
            throw new ServiceException("施工项目不存在");
        }

        Project project = projectService.lambdaQuery()
                .eq(Project::getConstructionProjectId, projectId).one();

        // 2. 组装VO返回
        ProjectDetailVO detailVO = new ProjectDetailVO();
        // 基础信息赋值
        detailVO.setProjectCode(projectDO.getProjectCode());
        detailVO.setProjectName(projectDO.getProjectName());
        detailVO.setCustomerName(projectDO.getCustomerName());
        detailVO.setCustomerPhone(projectDO.getCustomerPhone());
        detailVO.setProjectAddress(projectDO.getProjectAddress());
        detailVO.setStartDate(projectDO.getStartDate());
        detailVO.setExpectedCheckinDate(projectDO.getExpectedCheckinDate());
        detailVO.setProjectStatus(projectDO.getProjectStatus());
        detailVO.setPauseReason(projectDO.getPauseReason());
        detailVO.setRemark(projectDO.getRemark());
        if (project != null) {
            detailVO.setCustomerId(project.getH5UserId());
            detailVO.setProjectId(project.getId());
        }
        return detailVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateProjectStatus(Long projectId, Integer status, String pauseReason, String operator) {
        // 1. 校验参数
        if (status == null || (status != 1 && status != 2)) {
            throw new ServiceException("项目状态只能是“进行中（1）”或“暂停（2）”");
        }
        if (status == 2 && (pauseReason == null || pauseReason.trim().isEmpty())) {
            throw new ServiceException("暂停状态需填写暂停原因");
        }

        // 2. 校验项目是否存在
        ConstructionProjectDO existProject = getById(projectId);
        if (existProject == null) {
            throw new ServiceException("施工项目不存在");
        }

        // 3. 更新状态
        ConstructionProjectDO updateDO = new ConstructionProjectDO();
        updateDO.setId(projectId);
        updateDO.setProjectStatus(status);
        updateDO.setPauseReason(status == 2 ? pauseReason : null);
        updateDO.setUpdateBy(operator);
        updateById(updateDO);
    }

    @Override
    public String generateProjectCode() {
        // 1. 获取当前年份（如2025）
        String year = LocalDate.now().format(YEAR_FORMATTER);
        // 2. 查询当前年份最大流水号
        Integer maxSeq = baseMapper.getMaxProjectCodeSeq(year);
        // 3. 生成6位流水号（默认从000001开始）
        String seqStr = String.format("%06d", (maxSeq == null ? 1 : maxSeq + 1));
        // 4. 拼接施工编号（SG + 年份 + 6位流水号）
        return "GC" + year + seqStr;
    }

    @Override
    public ProjectDetailVO getProjectByH5UserId(Long h5UserId) {

        Project project = projectService.lambdaQuery()
                .eq(Project::getH5UserId, h5UserId)
                .eq(Project::getDeleted, 0)
                .ne(Project::getProgress, 8)
                .one();
        if (project != null) {
            String designerName = project.getDesignerName();
            BigDecimal buildingArea = project.getBuildingArea();
            Long constructionProjectId = project.getConstructionProjectId();
            ConstructionProjectDO constructionProject = this.lambdaQuery()
                    .eq(ConstructionProjectDO::getId, constructionProjectId)
                    .one();
            if (constructionProject != null) {
                ProjectDetailVO projectDetailVO = ContructionProjectConverter.INSTANCE.doToDetailVo(constructionProject);
                projectDetailVO.setDesignerName(designerName);
                projectDetailVO.setBuildingArea(buildingArea);
                List<ConstructionProjectStageRelDO> stageRelDOS = projectStageService.lambdaQuery()
                        .eq(ConstructionProjectStageRelDO::getProjectId, constructionProjectId)
                        .list();
                List<StageDetailVO> stageDetailVOS = StageConverter.INSTANCE.doListToVOList(stageRelDOS);
                List<ConstructionProjectItemDO> constructionProjectItemDOS = projectItemService.lambdaQuery()
                        .eq(ConstructionProjectItemDO::getProjectId, constructionProjectId)
                        .list();
                Map<Long, List<ConstructionProjectItemDO>> stageIdToItemsMap = constructionProjectItemDOS.stream()
                        .collect(Collectors.groupingBy(ConstructionProjectItemDO::getStageId));

                stageDetailVOS.forEach(x -> {
                    ConstructionStageDO stageDO = stageService.getById(x.getStageId());
                    x.setStageName(stageDO.getStageName());
                    List<ConstructionProjectItemDO> stageItems = stageIdToItemsMap.get(x.getStageId());
                    List<TaskDetailVO> taskDetailVOS = ProjectItemConverter.INSTANCE.doToVo(stageItems);
                    x.setTaskList(taskDetailVOS);
                });
                projectDetailVO.setStageDetailList(stageDetailVOS);
                return projectDetailVO;
            }
        }

        return null;
    }

    @Override
    public long countUnderConstructionProjects() {
        LambdaQueryWrapper<ConstructionProjectDO> wrapper = new LambdaQueryWrapper<ConstructionProjectDO>()
                .eq(ConstructionProjectDO::getIsDeleted, 0)
                .eq(ConstructionProjectDO::getProjectStatus, 1); // 进行中
        return this.count(wrapper);
    }

    /**
     * 校验项目日期逻辑（开工日期≥当前日期，预计入住日期>开工日期）
     */
    private void validateProjectDate(LocalDate startDate, LocalDate expectedCheckinDate) {
        LocalDate now = LocalDate.now();
        // 开工日期需≥当前日期
        if (startDate.isBefore(now)) {
            throw new ServiceException("开工日期不能早于当前日期");
        }
        // 预计入住日期需>开工日期（若填写）
        if (expectedCheckinDate != null && expectedCheckinDate.isBefore(startDate)) {
            throw new ServiceException("预计入住日期需晚于开工日期");
        }
    }

    /**
     * DTO 转 DO（基础字段映射）
     */
    private ConstructionProjectDO convertToDO(ContructionProjectAddOrUpdateDTO dto) {
        ConstructionProjectDO projectDO = new ConstructionProjectDO();
        projectDO.setId(dto.getId());
        projectDO.setApprovalProjectCode(dto.getApprovalProjectCode());
        projectDO.setProjectName(dto.getProjectName());
        projectDO.setCustomerName(dto.getCustomerName());
        projectDO.setCustomerPhone(dto.getCustomerPhone());
        projectDO.setProjectAddress(dto.getProjectAddress());
        projectDO.setStartDate(dto.getStartDate());
        projectDO.setExpectedCheckinDate(dto.getExpectedCheckinDate());
        projectDO.setRemark(dto.getRemark());
        return projectDO;
    }
}

